/**
 * WwallsController
 */
function WwallsController(view) {
	this.view = view;
}

WwallsController.prototype.startup = function(board) {
	this._board = board;
	this.view.init(this);
	this.redraw();

	this._board.onChange(function(movedBlock) {
		this.redraw();
		this._checkSolved(movedBlock);
	}.bind(this));
};

WwallsController.prototype._checkSolved = function(movedBlock) {
	var boardWidth = this._board.width();
	function touchesRightBound(block) {
		var lastSquareX = block.x() + block.length() - 1;
		return lastSquareX == boardWidth - 1;
	}

	if (movedBlock.pivot() && touchesRightBound(movedBlock)) {
		new AEnigmatesStorage("woodenblocks").solved();
		this.view.onSolved();
	}
};

WwallsController.prototype.redraw = function() {
	this.view.update(this._board.blocks());
};

WwallsController.prototype.blocks = function() {
	return this._board.blocks();
};

/**
 * WBoard
 */
function WBoard(w, h) {
	this._blocks = [];
	this._w = (typeof w === 'undefined') ? 6 : w;
	this._h = (typeof h === 'undefined') ? 6 : h;

	this._changedHandler = function() {
	};
}

WBoard.prototype.width = function() {
	return this._w;
};

WBoard.prototype.blocks = function() {
	return this._blocks;
};

WBoard.prototype.onChange = function(handler) {
	this._changedHandler = handler;
};

WBoard.prototype.blockBuilder = function() {
	var buondsHelper = null;
	var collisionDetector = new CollisionDetector(this._blocks);

	var blockState = {};
	var BlockConstructor = null;

	var self = this;
	var buildStep = {
		build : function() {
			var block = new BlockConstructor(blockState, buondsHelper, collisionDetector, function(movedBlock) {
				self._changedHandler(movedBlock);
			});
			self._blocks.push(block);

			if (!block.isVertical() && block.y() == Math.floor(self._h / 2) - 1) {
				block.pivot(true);
				self._pivotBlock = block;
			}

			return block;
		}
	};

	var lenStep = {
		len : function(len) {
			blockState.len = len;
			return buildStep;
		}
	};

	var xyStep = {
		xy : function(x, y) {
			blockState.x = x;
			blockState.y = y;

			return lenStep;
		}
	};

	var directionStep = {
		vertical : function() {
			blockState.direction = 'vertical';
			BlockConstructor = VWBlock;
			buondsHelper = new BoundsHelper(0, self._h);

			return xyStep;
		},
		horizontal : function() {
			blockState.direction = 'horizontal';
			BlockConstructor = HWBlock;
			buondsHelper = new BoundsHelper(0, self._w);

			return xyStep;
		}
	};

	return directionStep;
};

/**
 * BoundsHelper
 */
function BoundsHelper(lowerB, upperB) {
	this._lowerB = (typeof lowerB === 'undefined') ? 0 : lowerB;
	this._upperB = (typeof upperB === 'undefined') ? 6 : upperB;
}

BoundsHelper.prototype.inBounds = function(a) {
	return (a >= this._lowerB) && (a < this._upperB);
};

/**
 * CollisionDetector
 */
function CollisionDetector(blocks) {
	this._blocks = blocks;
}

CollisionDetector.prototype.squareOccupied = function(x, y) {
	var result = false;

	var blocks = this._blocks;
	for (var i = 0; i < blocks.length; i++) {
		var block = blocks[i];
		if (block.intersect(x, y)) {
			result = true;
			break;
		}
	}

	return result;
};

/**
 * WBlock
 */
function WBlock(state, boundsHelper, collisionDetector, movedHandler) {
	this._state = state;
	this._boundsHelper = boundsHelper;
	this._collisionDetector = collisionDetector;
	this._movedHandler = movedHandler;
	this._pivot = false;
}

WBlock.prototype.pivot = function(pivot) {
	if (typeof pivot !== 'undefined') {
		this._pivot = pivot;
	}

	return this._pivot;
};

WBlock.prototype._inBounds = function(a) {
	return this._boundsHelper.inBounds(a);
};

WBlock.prototype._pos = function() {
	return this._state[this._coord()];
};

WBlock.prototype.move = function(direction) {
	if (this.canMove(direction)) {
		this._state[this._coord()] += direction;
		this._movedHandler(this);
	}
};

WBlock.prototype.canMove = function(direction) {
	return this._inBoundsAfterMove(direction) && !this._collisionAfterMove(direction);
};

WBlock.prototype._inBoundsAfterMove = function(direction) {
	var position = this._state[this._coord()];
	var len = this._state.len;

	return this._inBounds(position + direction) && this._inBounds(position + (len - 1) + direction);
};

WBlock.prototype.length = function() {
	return this._state.len;
};

WBlock.prototype.x = function() {
	return this._state.x;
};

WBlock.prototype.y = function() {
	return this._state.y;
};

/**
 * Vertical WBlock
 */
function VWBlock(state) {
	WBlock.apply(this, arguments);
}

VWBlock.prototype = Object.create(WBlock.prototype);
VWBlock.prototype.constructor = VWBlock;

VWBlock.prototype._coord = function() {
	return 'y';
};

VWBlock.prototype.isVertical = function() {
	return true;
};

VWBlock.prototype.intersect = function(x, y) {
	var onSameCol = (x === this._state.x);

	var currY = this._state.y;
	var len = this._state.len;
	var inside = (y >= currY) && (y < currY + len);

	return onSameCol && inside;
};

VWBlock.prototype._collisionAfterMove = function(direction) {
	var result = false;
	var x = this._state.x;

	if (direction > 0) {
		var len = this._state.len;
		var y = this._state.y + (len - 1) + direction;
		result = this._collisionDetector.squareOccupied(x, y);
	} else if (direction < 0) {
		var y = this._state.y + direction;
		result = this._collisionDetector.squareOccupied(x, y);
	}

	return result;
};

/**
 * Horizontal WBlock
 */
function HWBlock() {
	WBlock.apply(this, arguments);
}

HWBlock.prototype = Object.create(WBlock.prototype);
HWBlock.prototype.constructor = HWBlock;

HWBlock.prototype._coord = function() {
	return 'x';
};

HWBlock.prototype.isVertical = function() {
	return false;
};

HWBlock.prototype.intersect = function(x, y) {
	var onSameRow = (y === this._state.y);

	var currX = this._state.x;
	var len = this._state.len;
	var inside = (x >= currX) && (x < currX + len);

	return onSameRow && inside;
};

HWBlock.prototype._collisionAfterMove = function(direction) {
	var result = false;
	var y = this._state.y;

	if (direction > 0) {
		var len = this._state.len;
		var x = this._state.x + (len - 1) + direction;
		result = this._collisionDetector.squareOccupied(x, y);
	} else if (direction < 0) {
		var x = this._state.x + direction;
		result = this._collisionDetector.squareOccupied(x, y);
	}

	return result;
};